/*******************************************************************************
*   Filename:       bsp_sht3x_dis.c
*   Revised:        All copyrights reserved to Roger.
*   Date:           2016-09-24
*   Revision:       v1.0
*   Writer:	     Roger-WY.
*
*   Description:    数字式温湿度传感器模块驱动
*
*
*   Notes:          尺寸： 2.5 mm×2.5 mm×0.9 mm
*                   输出：I2C，电压输出
*                   电源电压范围：2.4至5.5 V
*                   能耗：4.8μW（低重复性，1次测量/秒）
*                   RH工作范围：0 - 100% RH
*                   T工作范围：-40至+125℃（-40至+257℉）
*                   RH响应时间：8秒（tau63%）
*******************************************************************************
*SHT30三种采集数据方式：
* 1，Stretch阻塞模式，发送命令后采集，需要长时间等待SCL拉高，才能发送读取头然后读取数据
* 2，Polling非阻塞模式，发送命令后采集，需要反复多次启动并发送读取头，得到ACK以后才能读取数据
* 3，内部定期采集模式，启动时发送Periodic命令，读取时发送FetchData命令后直接读取数据
*
*
*   All copyrights reserved to Roger-WY
*******************************************************************************/
#include "bsp_sht3x_dis.h"
#include "bsp_timer.h"
#ifdef    SHT3XDIS_USE_HARD_I2C
#include "bsp_cpu_i2c2.h"
#else
#include "bsp_i2c_gpio.h"
#endif

/*硬件复位和报警的引脚使能在.h文件中配置*/


//----------------------------------------------------------------------------//
#ifdef   EN_SHT3X_NRESET   /* SHT3X 传感器的硬件复位引脚 */
#define  SHT3X_NRST_GPIO_RCC()  __HAL_RCC_GPIOA_CLK_ENABLE()          
#define  SHT3X_NRST_GPIO_PIN    GPIO_PIN_2
#define  SHT3X_NRST_GPIO_PORT   GPIOA

#define  SHT3X_NRST_1()  SHT3X_NRST_GPIO_PORT->BSRR = SHT3X_NRST_GPIO_PIN		/* NRESET = 1 */
#define  SHT3X_NRST_0()  SHT3X_NRST_GPIO_PORT->BRR  = SHT3X_NRST_GPIO_PIN	/* NRESET = 0 */
#endif

#ifdef   EN_SHT3X_ALERT   /* SHT3X 传感器的报警引脚 */
#define  SHT3X_ALERT_GPIO_RCC()   __HAL_RCC_GPIOA_CLK_ENABLE()          
#define  SHT3X_ALERT_GPIO_PIN    GPIO_PIN_2
#define  SHT3X_ALERT_GPIO_PORT   GPIOA

#define  SHT3X_ALERT_READ()  ((SHT3X_ALERT_GPIO_PORT->IDR &SHT3X_ALERT_GPIO_PIN) != 0)
#endif

/*SHT3x使用的ms级延时函数，须由用户提供。*/
#define Sht3xDelayMs(_nms)  bsp_DelayMS(_nms)

//----------------------------------------------------------------------------//

//============================================================================//
#if defined(SHT3XDIS_USE_HARD_I2C)
static int8_t sht3x_SendCmd(uint8_t sht3xI2cAddr,etSht3xCommands cmd)
{
    HAL_StatusTypeDef halStatus;
    uint8_t cmd_buffer[2] = {0};
    cmd_buffer[0] = cmd >> 8;
    cmd_buffer[1] = cmd & 0xFF;
    
    halStatus = HAL_I2C_Master_Transmit(&halI2c2, (sht3xI2cAddr << 1) | 0x00, cmd_buffer, 2, I2Cx_WAIT_TIMEOUT);

    if(HAL_OK == halStatus)
    {
        return NO_ERROR;
    }
    else
    {
        return ACK_ERROR;
    }   
}

static uint8_t sht3x_RecByte(uint8_t sht3xI2cAddr,etSht3xCommands cmd,uint8_t *pbyte,uint8_t number)
{
    sht3x_SendCmd(sht3xI2cAddr,cmd);
    return(HAL_I2C_Master_Receive(&halI2c2, (sht3xI2cAddr << 1) | 0x01, pbyte, number, I2Cx_WAIT_TIMEOUT));
}

#else   //通过IO模拟I2C

static int8_t sht3x_SendCmd(uint8_t sht3xI2cAddr,etSht3xCommands cmd)
{
    uint8_t ack = 0;
    bsp_i2c_Start();
    bsp_i2c_SendByte((sht3xI2cAddr << 1) | 0x00);
    ack = bsp_i2c_WaitAck();
    if(ack != 0)
    {
        goto err_exit;
    }
    bsp_i2c_SendByte(cmd >> 8);
    ack = bsp_i2c_WaitAck();
    if(ack != 0)
    {
        goto err_exit;
    }
    bsp_i2c_SendByte(cmd & 0xFF);
    ack = bsp_i2c_WaitAck();
    if(ack != 0)
    {
        goto err_exit;
    }
    err_exit:
    bsp_i2c_Stop();
    return ack;
}

static uint8_t sht3x_RecByte(uint8_t sht3xI2cAddr,etSht3xCommands cmd,uint8_t *pbyte,uint8_t number)
{
    uint8_t ack = 0;
    uint8_t i = 0;
    
    bsp_i2c_Start();
    bsp_i2c_SendByte((sht3xI2cAddr << 1) | 0x00);
    ack = bsp_i2c_WaitAck();
    if(ack != 0)
    {
        goto err_exit;
    }
    bsp_i2c_SendByte(cmd >> 8);
    ack = bsp_i2c_WaitAck();
    if(ack != 0)
    {
        goto err_exit;
    }
    bsp_i2c_SendByte(cmd & 0xFF);
    ack = bsp_i2c_WaitAck();
    if(ack != 0)
    {
        goto err_exit;
    }
    
    bsp_i2c_Start();
    bsp_i2c_SendByte((sht3xI2cAddr << 1) | 0x01);  //读
    ack = bsp_i2c_WaitAck();
    
    for(i = 0; i < number ; i++) {
      if(i == (number - 1)) {
          *pbyte = bsp_i2c_ReadByte();    
          bsp_i2c_NAck();     //读完数据之后不发送应答信号
        } else {
          *pbyte = bsp_i2c_ReadByte();     //读完数据之后发送应答信号
          bsp_i2c_Ack();  
          pbyte++;
        }
    }

    err_exit:
    bsp_i2c_Stop();
    return ack;
}

#endif


void bsp_Sht3xInit(uint8_t sht3xI2cAddr)
{
    #if defined(SHT3XDIS_USE_HARD_I2C)
    hal_I2c2Master_Init();
    #else
    bsp_InitI2C();
    #endif
    
    bsp_Sht3xSoftReset(sht3xI2cAddr);
    
    #if 1
    static uint32_t sht3xSerialNbr = 0;
    bsp_SHT3xReadSerialNumber(sht3xI2cAddr,&sht3xSerialNbr);
    #endif
    
    /* 传感器设置为周期性读取模式 高可重复性 频率为1HZ */
    bsp_Sht3xStartPeriodicMeasurment(sht3xI2cAddr,REPEATAB_HIGH,FREQUENCY_1HZ);
    
}

/*******************************************************************************
* 名    称： SHT3X_CalcTemperature
* 功    能： SHT3X计算温度值
* 入口参数： 原始温度值
* 出口参数： 当前温度值
* 作　　者： Roger-WY.
* 创建日期： 2021-05-21
* 修    改：
* 修改日期：
* 备    注： 单位: ℃
*******************************************************************************/
float SHT3X_CalcTemperature(uint16_t rawValue)
{
    // calculate temperature [℃]
    return 175 * (float)rawValue / 65535 - 45; // T = -45 + 175 * rawValue / (2^16-1)
}

/*******************************************************************************
* 名    称： SHT3X_CalcHumidity
* 功    能： SHT3x计算当前湿度
* 入口参数： 湿度原始值
* 出口参数： 当前湿度值
* 作　　者： Roger-WY.
* 创建日期： 2021-05-21
* 修    改：
* 修改日期：
* 备    注：
*******************************************************************************/
float SHT3X_CalcHumidity(uint16_t rawValue)
{
    // calculate relative humidity [%RH]
    return 100 * (float)rawValue / 65535;  // RH = rawValue / (2^16-1) * 100
}

/*******************************************************************************
* 名    称： SHT3X_CalcCrc
* 功    能： 计算CRC-8的值
* 入口参数： uint8_t *data：     输入的字节
*            uint8_t nbrOfBytes  输入的字节数
* 出口参数： 计算出的CRC-8校验值
* 作　　者： Roger-WY.
* 创建日期： 2016-10-17
* 修    改：
* 修改日期：
* 备    注：
*******************************************************************************/
uint8_t SHT3X_CalcCrc(uint8_t *data, uint8_t nbrOfBytes)
{
    /*CRC*/
    #define SHT3X_POLYNOMIAL  0x131           // P(x) = x^8 + x^5 + x^4 + 1 = 100110001

	uint8_t bit;        // bit mask
    uint8_t crc = 0xFF; // calculated checksum
    uint8_t byteCtr;    // byte counter

    // calculates 8-Bit checksum with given polynomial
    for(byteCtr = 0; byteCtr < nbrOfBytes; byteCtr++) {
        crc ^= (data[byteCtr]);
        for(bit = 8; bit > 0; --bit) {
            if(crc & 0x80) {
                crc = (crc << 1) ^ SHT3X_POLYNOMIAL;
            }  else {
                crc = (crc << 1);
            }
        }
    }
	return crc;
}

/*******************************************************************************
* 名    称： SHT3X_CheckCrc
* 功    能： SHT3x检查CRC校验是否通过
* 入口参数： uint8_t *pdata：     输入的字节
*            uint8_t nbrOfBytes： 输入的字节数
*            uint8_t checksum：   当前校验值
* 出口参数： 1：没有通过校验    0：校验没有问题
* 作　　者： Roger-WY.
* 创建日期： 2016-10-17
* 修    改：
* 修改日期：
* 备    注：
*******************************************************************************/
uint8_t SHT3X_CheckCrc(uint8_t *pdata, uint8_t nbrOfBytes, uint8_t checksum)
{
    uint8_t crc;
	crc = SHT3X_CalcCrc(pdata, nbrOfBytes);// calculates 8-Bit checksum
    if(crc != checksum) {               // verify checksum
        return CHECKSUM_ERROR;          // 检验没有通过
    }
    return NO_ERROR;                    // 校验没有问题
}

/*******************************************************************************
* 名    称： bsp_Sht3xSoftReset
* 功    能： SHT3x软复位
* 入口参数： 无
* 出口参数： 无
* 作　　者： Roger-WY.
* 创建日期： 2016-10-17
* 修    改：
* 修改日期：
* 备    注：
*******************************************************************************/
uint8_t bsp_Sht3xSoftReset(uint8_t sht3xI2cAddr)
{
    if(sht3x_SendCmd(sht3xI2cAddr,CMD_SOFT_RESET) == NO_ERROR)
    {
        Sht3xDelayMs(10);   //延时10ms等待传感器复位完成
        return NO_ERROR;
    }
    else
    {
        return ACK_ERROR;
    }
}

/*******************************************************************************
* 名    称： bsp_SHT3xReadSerialNumber
* 功    能： 读取SHT3X的SN号
* 入口参数： *serialNbr  SHT3X的SN号
* 出口参数： 无
* 作　　者： Roger-WY.
* 创建日期： 2016-10-17
* 修    改：
* 修改日期：
* 备    注：
*******************************************************************************/
uint8_t bsp_SHT3xReadSerialNumber(uint8_t sht3xI2cAddr,uint32_t *serialNbr)
{
    uint8_t  error; // error code
    uint8_t serialNumWords[6] = {0};
 
    //error = sht3x_SendCmd(sht3xI2cAddr,CMD_READ_SERIALNBR);
    error = sht3x_RecByte(sht3xI2cAddr,CMD_READ_SERIALNBR,serialNumWords,6);
    if((SHT3X_CheckCrc(serialNumWords,2,serialNumWords[2]) == NO_ERROR) && (SHT3X_CheckCrc(&serialNumWords[3],2,serialNumWords[5]) == NO_ERROR))
    {
        *serialNbr = (uint32_t)((((uint16_t)(serialNumWords[0]) << 8) + (uint16_t)(serialNumWords[1])) << 16) | (uint32_t)((uint16_t)(serialNumWords[3]) << 8) + (uint16_t)(serialNumWords[4]);
        error = NO_ERROR;
    }
    else
    {
        error = CHECKSUM_ERROR;          // 检验没有通过
    }
    return error;
}

/*******************************************************************************
* 名    称： bsp_Sht3xStartPeriodicMeasurment
* 功    能： 内部定期采集模式，根据设置的内部读取的频率。
* 入口参数： etRepeatab repeatab：重复性
*            etFrequency    freq: 读取频率
* 出口参数： 无
* 作　　者： Roger-WY.
* 创建日期： 2016-10-17
* 修    改：
* 修改日期：
* 备    注：内部定期采集模式，启动时发送Periodic命令，读取时发送FetchData命令后直接读取数据
*******************************************************************************/
uint8_t bsp_Sht3xStartPeriodicMeasurment(uint8_t sht3xI2cAddr,etSht3xRepeatab  repeatab,etSht3xFrequency freq)
{
    uint8_t error;        // error code

    // use depending on the required repeatability and frequency,
    // the corresponding command
    switch(repeatab) {
        case REPEATAB_LOW: // low repeatability
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_POLLING_L);
            //此处需要增加适当延时，否则硬件i2c没有收到结束信息,无法正确设置读取速率
            Sht3xDelayMs(1);
        switch(freq) {
            case FREQUENCY_HZ5:  // low repeatability,  0.5 Hz
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_PERI_05_L); break;
            case FREQUENCY_1HZ:  // low repeatability,  1.0 Hz
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_PERI_1_L); break;
            case FREQUENCY_2HZ:  // low repeatability,  2.0 Hz
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_PERI_2_L); break;
            case FREQUENCY_4HZ:  // low repeatability,  4.0 Hz
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_PERI_4_L); break;
            case FREQUENCY_10HZ: // low repeatability, 10.0 Hz
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_PERI_10_L); break;
            default:
            error |= PARM_ERROR; break;
        }
        break;

        case REPEATAB_MEDIUM: // medium repeatability
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_POLLING_M);
            //此处需要增加适当延时，否则硬件i2c没有收到结束信息,无法正确设置读取速率
            Sht3xDelayMs(1);
        switch(freq) {
            case FREQUENCY_HZ5:  // medium repeatability,  0.5 Hz
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_PERI_05_M); break;
            case FREQUENCY_1HZ:  // medium repeatability,  1.0 Hz
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_PERI_1_M); break;
            case FREQUENCY_2HZ:  // medium repeatability,  2.0 Hz
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_PERI_2_M); break;
            case FREQUENCY_4HZ:  // medium repeatability,  4.0 Hz
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_PERI_4_M); break;
            case FREQUENCY_10HZ: // medium repeatability, 10.0 Hz
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_PERI_10_M); break;
            default:
            error |= PARM_ERROR; break;
        }
        break;

        case REPEATAB_HIGH: // high repeatability
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_POLLING_H);
            //此处需要增加适当延时，否则硬件i2c没有收到结束信息,无法正确设置读取速率
            Sht3xDelayMs(1);
        switch(freq) {
            case FREQUENCY_HZ5:  // high repeatability,  0.5 Hz
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_PERI_05_H); break;
            case FREQUENCY_1HZ:  // high repeatability,  1.0 Hz
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_PERI_1_H); break;
            case FREQUENCY_2HZ:  // high repeatability,  2.0 Hz
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_PERI_2_H); break;
            case FREQUENCY_4HZ:  // high repeatability,  4.0 Hz
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_PERI_4_H); break;
            case FREQUENCY_10HZ: // high repeatability, 10.0 Hz
            error = sht3x_SendCmd(sht3xI2cAddr,CMD_MEAS_PERI_10_H); break;
            default:
            error |= PARM_ERROR; break;
        }
        break;
        default:
        error |= PARM_ERROR; break;
    }

    return error;
}

/*******************************************************************************
* 名    称： bsp_Sht3xReadFetchData
* 功    能： 读取传感器内部周期性测量的温湿度的值
* 入口参数： byte：需要发送的字节
* 出口参数： 无
* 作　　者： Roger-WY.
* 创建日期： 2016-10-17
* 修    改：
* 修改日期：
* 备    注：调用该函数之前务必调用bsp_Sht3xStartPeriodicMeasurment(etRepeatab  repeatab,etFrequency freq)
*           设置内部采集的重复性和频率。
*******************************************************************************/
uint8_t bsp_Sht3xReadFetchData(uint8_t sht3xI2cAddr,stSht3xValue *psht3xvalue)
{
    uint8_t  error = TIMEOUT_ERROR;        // error code
    uint8_t dataBuffer[6] = {0};
	uint16_t     rawValueTemp; // temperature raw value from sensor
    uint16_t     rawValueHumi; // humidity raw value from sensor

	//error = sht3x_SendCmd(sht3xI2cAddr,CMD_FETCH_DATA);
    error = sht3x_RecByte(sht3xI2cAddr,CMD_FETCH_DATA,dataBuffer,6);
    error = SHT3X_CheckCrc(dataBuffer,2,dataBuffer[2]);
    if(error == NO_ERROR)
    {
        rawValueTemp = ((uint16_t)(dataBuffer[0]) << 8) + (uint16_t)(dataBuffer[1]);
        psht3xvalue->fTemp =  SHT3X_CalcTemperature(rawValueTemp);
        psht3xvalue->iTemp = (int16_t)(psht3xvalue->fTemp * 10);//将浮点型的数据转换为整型
    }
    
    error = SHT3X_CheckCrc(&dataBuffer[3],2,dataBuffer[5]);
    if(error == NO_ERROR)
    {
        rawValueHumi = ((uint16_t)(dataBuffer[3]) << 8) + (uint16_t)(dataBuffer[4]);
        psht3xvalue->fHumi =  SHT3X_CalcHumidity(rawValueHumi);
        psht3xvalue->iHumi = (uint16_t)(psht3xvalue->fHumi * 10);//将浮点型的数据转换为整型
    }

	return error;
}

/*******************************************************************************
* 名    称： Bsp_Sht3xReadStatus
* 功    能： SHT3x读取状态
* 入口参数： *status：状态值
* 出口参数： 是否有错误
* 作　　者： Roger-WY.
* 创建日期： 2016-10-17
* 修    改：
* 修改日期：
* 备    注：
*******************************************************************************/
uint8_t bsp_Sht3xReadStatus(uint8_t sht3xI2cAddr,utSht3xRegStatus *status)
{
    uint8_t error; // error code
    uint8_t statusBuffer[3] = {0};

    //error = sht3x_SendCmd(sht3xI2cAddr,CMD_READ_STATUS);
    error = sht3x_RecByte(sht3xI2cAddr,CMD_READ_STATUS,statusBuffer,3);
    
    error = SHT3X_CheckCrc(statusBuffer,2,statusBuffer[2]);
    if(error == NO_ERROR)
    {
        status->u16RegStatus = ((uint16_t)(statusBuffer[0]) << 8) + (uint16_t)(statusBuffer[1]);
    }

    return error;
}

/*******************************************************************************
* 名    称： Bsp_Sht3xClearAllAlertFlags
* 功    能： SHT3x清除所有的警报标志位
* 入口参数： 无
* 出口参数： 无
* 作　　者： Roger-WY.
* 创建日期： 2016-10-17
* 修    改：
* 修改日期：
* 备    注：
*******************************************************************************/
uint8_t bsp_Sht3xClearAllAlertFlags(uint8_t sht3xI2cAddr)
{
    uint8_t error; // error code

    error = sht3x_SendCmd(sht3xI2cAddr,CMD_CLEAR_STATUS);

    return error;
}


/*******************************************************************************
* 名    称： bsp_Sht3xEnableHeater
* 功    能： 使能SHT3X加热
* 入口参数： 无
* 出口参数： 无
* 作　　者： Roger-WY.
* 创建日期： 2016-10-17
* 修    改：
* 修改日期：
* 备    注：
*******************************************************************************/
uint8_t bsp_Sht3xEnableHeater(uint8_t sht3xI2cAddr)
{
    uint8_t error; // error code

    error = sht3x_SendCmd(sht3xI2cAddr,CMD_HEATER_ENABLE);

    return error;
}

/*******************************************************************************
* 名    称： Bsp_Sht3xDisbaleHeater
* 功    能： 失能SHT3X加热
* 入口参数： 无
* 出口参数： 无
* 作　　者： Roger-WY.
* 创建日期： 2016-10-17
* 修    改：
* 修改日期：
* 备    注：
*******************************************************************************/
uint8_t Bsp_Sht3xDisbaleHeater(uint8_t sht3xI2cAddr)
{
    uint8_t error; // error code

    error = sht3x_SendCmd(sht3xI2cAddr,CMD_HEATER_DISABLE);

    return error;
}






#ifdef EN_SHT3X_NRESET     //系统是否使能硬件复位引脚
/*******************************************************************************
* 名    称： Bsp_Sht3x_HardReset
* 功    能： SHT3x硬件复位引脚
* 入口参数： 无
* 出口参数： 无
* 作　　者： Roger-WY.
* 创建日期： 2016-10-17
* 修    改：
* 修改日期：
* 备    注： SHT3x硬件复位，模块中该引脚没有引出，用户自己可以引出该引脚。
*******************************************************************************/
void bsp_Sht3xHardReset(void)
{
    SHT3X_NRST_0();     // set reset low
    /*拉低nRESET引脚的电平（至少350ns）将会产生硬复位信号重置传感器。*/
	Sht3xDelayMs(1);   // wait 1 ms
	SHT3X_NRST_1();     // release reset
	Sht3xDelayMs(50);   // wait 50 ms afloater reset
}
#endif

#ifdef   EN_SHT3X_ALERT   /* SHT3X 传感器的报警引脚 */
/*******************************************************************************
* 名    称： SHT3X_ReadAlert
* 功    能： 读取SHT3x温湿度传感器的报警引脚
* 入口参数： 无
* 出口参数： 无
* 作　　者： Roger-WY.
* 创建日期： 2016-10-17
* 修    改：
* 修改日期：
* 备    注： 此处使用轮询的方式，用户也可以使用中断的方式处理
*******************************************************************************/
uint8_t bsp_Sht3xReadAlert(void)
{
    return ((SHT3X_ALERT_READ() != 0) ? TRUE : FALSE);
}
#endif

/***************************** (END OF FILE) **********************************/
